/**
* Dropdown component.
*
* @author Htmlstream
* @version 1.0
*/
;
(function ($) {
'use strict';
$.HSCore.components.HSDropdown = {
/**
* Base configuration of the component.
*
* @private
*/
_baseConfig: {
dropdownEvent: 'click',
dropdownType: 'simple',
dropdownDuration: 300,
dropdownEasing: 'linear',
dropdownAnimationIn: 'fadeIn',
dropdownAnimationOut: 'fadeOut',
dropdownHideOnScroll: true,
dropdownHideOnBlur: false,
dropdownDelay: 350,
afterOpen: function (invoker) {
},
afterClose: function (invoker) {
}
},
/**
* Collection of all initialized items on the page.
*
* @private
*/
_pageCollection: $(),
/**
* Initialization.
*
* @param {jQuery} collection
* @param {Object} config
*
* @public
* @return {jQuery}
*/
init: function (collection, config) {
var self;
if (!collection || !collection.length) return;
self = this;
var fieldsQty;
collection.each(function (i, el) {
var $this = $(el), itemConfig;
if ($this.data('HSDropDown')) return;
itemConfig = config && $.isPlainObject(config) ?
$.extend(true, {}, self._baseConfig, config, $this.data()) :
$.extend(true, {}, self._baseConfig, $this.data());
switch (itemConfig.dropdownType) {
case 'css-animation' :
$this.data('HSDropDown', new DropdownCSSAnimation($this, itemConfig));
break;
case 'jquery-slide' :
$this.data('HSDropDown', new DropdownJSlide($this, itemConfig));
break;
default :
$this.data('HSDropDown', new DropdownSimple($this, itemConfig));
}
self._pageCollection = self._pageCollection.add($this);
self._bindEvents($this, itemConfig.dropdownEvent, itemConfig.dropdownDelay);
var DropDown = $(el).data('HSDropDown');
fieldsQty = $(DropDown.target).find('input, textarea').length;
});
$(document).on('keyup.HSDropDown', function (e) {
if (e.keyCode && e.keyCode == 27) {
self._pageCollection.each(function (i, el) {
var windW = $(window).width(),
optIsMobileOnly = Boolean($(el).data('is-mobile-only'));
if (!optIsMobileOnly) {
$(el).data('HSDropDown').hide();
} else if (optIsMobileOnly && windW < 769) {
$(el).data('HSDropDown').hide();
}
});
}
});
$(window).on('click', function (e) {
self._pageCollection.each(function (i, el) {
var windW = $(window).width(),
optIsMobileOnly = Boolean($(el).data('is-mobile-only'));
if (!optIsMobileOnly) {
$(el).data('HSDropDown').hide();
} else if (optIsMobileOnly && windW < 769) {
$(el).data('HSDropDown').hide();
}
});
});
self._pageCollection.each(function (i, el) {
var target = $(el).data('HSDropDown').config.dropdownTarget;
$(target).on('click', function(e) {
e.stopPropagation();
});
});
$(window).on('scroll.HSDropDown', function (e) {
self._pageCollection.each(function (i, el) {
var DropDown = $(el).data('HSDropDown');
if (DropDown.getOption('dropdownHideOnScroll') && fieldsQty === 0) {
DropDown.hide();
} else if (DropDown.getOption('dropdownHideOnScroll') && !(/iPhone|iPad|iPod/i.test(navigator.userAgent))) {
DropDown.hide();
}
});
});
$(window).on('resize.HSDropDown', function (e) {
if (self._resizeTimeOutId) clearTimeout(self._resizeTimeOutId);
self._resizeTimeOutId = setTimeout(function () {
self._pageCollection.each(function (i, el) {
var DropDown = $(el).data('HSDropDown');
DropDown.smartPosition(DropDown.target);
});
}, 50);
});
return collection;
},
/**
* Binds necessary events.
*
* @param {jQuery} $invoker
* @param {String} eventType
* @param {Number} delay
* @private
*/
_bindEvents: function ($invoker, eventType, delay) {
var $dropdown = $($invoker.data('dropdown-target'));
// if (eventType == 'hover' && !_isTouch()) {
if (eventType == 'hover') {
$invoker.on('mouseenter.HSDropDown', function (e) {
var $invoker = $(this),
HSDropDown = $invoker.data('HSDropDown');
if (!HSDropDown) return;
if (HSDropDown.dropdownTimeOut) clearTimeout(HSDropDown.dropdownTimeOut);
HSDropDown.show();
})
.on('mouseleave.HSDropDown', function (e) {
var $invoker = $(this),
HSDropDown = $invoker.data('HSDropDown');
if (!HSDropDown) return;
HSDropDown.dropdownTimeOut = setTimeout(function () {
HSDropDown.hide();
}, delay);
});
if ($dropdown.length) {
$dropdown.on('mouseenter.HSDropDown', function (e) {
var HSDropDown = $invoker.data('HSDropDown');
if (HSDropDown.dropdownTimeOut) clearTimeout(HSDropDown.dropdownTimeOut);
HSDropDown.show();
})
.on('mouseleave.HSDropDown', function (e) {
var HSDropDown = $invoker.data('HSDropDown');
HSDropDown.dropdownTimeOut = setTimeout(function () {
HSDropDown.hide();
}, delay);
});
}
}
else {
$invoker.on('click.HSDropDown', function (e) {
var $curInvoker = $(this);
if (!$curInvoker.data('HSDropDown')) return;
if ($('[data-dropdown-target].active').length) {
$('[data-dropdown-target].active').data('HSDropDown').toggle();
}
$curInvoker.data('HSDropDown').toggle();
e.stopPropagation();
e.preventDefault();
});
}
}
};
function _isTouch() {
return 'ontouchstart' in window;
}
/**
* Abstract Dropdown class.
*
* @param {jQuery} element
* @param {Object} config
* @abstract
*/
function AbstractDropdown(element, config) {
if (!element.length) return false;
this.element = element;
this.config = config;
this.target = $(this.element.data('dropdown-target'));
this.allInvokers = $('[data-dropdown-target="' + this.element.data('dropdown-target') + '"]');
this.toggle = function () {
if (!this.target.length) return this;
if (this.defaultState) {
this.show();
}
else {
this.hide();
}
return this;
};
this.smartPosition = function (target) {
if (target.data('baseDirection')) {
target.css(
target.data('baseDirection').direction,
target.data('baseDirection').value
);
}
target.removeClass('u-dropdown--reverse-y');
var $w = $(window),
styles = getComputedStyle(target.get(0)),
direction = Math.abs(parseInt(styles.left, 10)) < 40 ? 'left' : 'right',
targetOuterGeometry = target.offset();
// horizontal axis
if (direction == 'right') {
if (!target.data('baseDirection')) target.data('baseDirection', {
direction: 'right',
value: parseInt(styles.right, 10)
});
if (targetOuterGeometry.left < 0) {
target.css(
'right',
(parseInt(target.css('right'), 10) - (targetOuterGeometry.left - 10 )) * -1
);
}
}
else {
if (!target.data('baseDirection')) target.data('baseDirection', {
direction: 'left',
value: parseInt(styles.left, 10)
});
if (targetOuterGeometry.left + target.outerWidth() > $w.width()) {
target.css(
'left',
(parseInt(target.css('left'), 10) - (targetOuterGeometry.left + target.outerWidth() + 10 - $w.width()))
);
}
}
// vertical axis
if (targetOuterGeometry.top + target.outerHeight() - $w.scrollTop() > $w.height()) {
target.addClass('u-dropdown--reverse-y');
}
};
this.getOption = function (option) {
return this.config[option] ? this.config[option] : null;
};
return true;
}
/**
* DropdownSimple constructor.
*
* @param {jQuery} element
* @param {Object} config
* @constructor
*/
function DropdownSimple(element, config) {
if (!AbstractDropdown.call(this, element, config)) return;
Object.defineProperty(this, 'defaultState', {
get: function () {
return this.target.hasClass('u-dropdown--hidden');
}
});
this.target.addClass('u-dropdown--simple');
this.hide();
}
/**
* Shows dropdown.
*
* @public
* @return {DropdownSimple}
*/
DropdownSimple.prototype.show = function () {
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').addClass('active');
this.smartPosition(this.target);
this.target.removeClass('u-dropdown--hidden');
if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'true');
this.config.afterOpen.call(this.target, this.element);
return this;
}
/**
* Hides dropdown.
*
* @public
* @return {DropdownSimple}
*/
DropdownSimple.prototype.hide = function () {
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
this.target.addClass('u-dropdown--hidden');
if (this.allInvokers.length) this.allInvokers.attr('aria-expanded', 'false');
this.config.afterClose.call(this.target, this.element);
return this;
}
/**
* DropdownCSSAnimation constructor.
*
* @param {jQuery} element
* @param {Object} config
* @constructor
*/
function DropdownCSSAnimation(element, config) {
if (!AbstractDropdown.call(this, element, config)) return;
var self = this;
this.target
.addClass('u-dropdown--css-animation u-dropdown--hidden')
.css('animation-duration', self.config.dropdownDuration + 'ms');
Object.defineProperty(this, 'defaultState', {
get: function () {
return this.target.hasClass('u-dropdown--hidden');
}
});
if (this.target.length) {
this.target.on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function (e) {
if (self.target.hasClass(self.config.dropdownAnimationOut)) {
self.target.removeClass(self.config.dropdownAnimationOut)
.addClass('u-dropdown--hidden');
if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'false');
self.config.afterClose.call(self.target, self.element);
}
if (self.target.hasClass(self.config.dropdownAnimationIn)) {
if (self.allInvokers.length) self.allInvokers.attr('aria-expanded', 'true');
self.config.afterOpen.call(self.target, self.element);
}
e.preventDefault();
e.stopPropagation();
});
}
}
/**
* Shows dropdown.
*
* @public
* @return {DropdownCSSAnimation}
*/
DropdownCSSAnimation.prototype.show = function () {
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').addClass('active');
this.smartPosition(this.target);
this.target.removeClass('u-dropdown--hidden')
.removeClass(this.config.dropdownAnimationOut)
.addClass(this.config.dropdownAnimationIn);
}
/**
* Hides dropdown.
*
* @public
* @return {DropdownCSSAnimation}
*/
DropdownCSSAnimation.prototype.hide = function () {
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
this.target.removeClass(this.config.dropdownAnimationIn)
.addClass(this.config.dropdownAnimationOut);
}
/**
* DropdownSlide constructor.
*
* @param {jQuery} element
* @param {Object} config
* @constructor
*/
function DropdownJSlide(element, config) {
if (!AbstractDropdown.call(this, element, config)) return;
this.target.addClass('u-dropdown--jquery-slide u-dropdown--hidden').hide();
Object.defineProperty(this, 'defaultState', {
get: function () {
return this.target.hasClass('u-dropdown--hidden');
}
});
}
/**
* Shows dropdown.
*
* @public
* @return {DropdownJSlide}
*/
DropdownJSlide.prototype.show = function () {
var self = this;
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').addClass('active');
this.smartPosition(this.target);
this.target.removeClass('u-dropdown--hidden').stop().slideDown({
duration: self.config.dropdownDuration,
easing: self.config.dropdownEasing,
complete: function () {
self.config.afterOpen.call(self.target, self.element);
}
});
}
/**
* Hides dropdown.
*
* @public
* @return {DropdownJSlide}
*/
DropdownJSlide.prototype.hide = function () {
var self = this;
var activeEls = $(this)[0].config.dropdownTarget;
$('[data-dropdown-target="' + activeEls + '"]').removeClass('active');
this.target.stop().slideUp({
duration: self.config.dropdownDuration,
easing: self.config.dropdownEasing,
complete: function () {
self.config.afterClose.call(self.target, self.element);
self.target.addClass('u-dropdown--hidden');
}
});
}
})(jQuery);